/*
 * @(#)JideButtonUI.java	Nov 20, 2002
 *
 * Copyright 2002 JIDE Software Inc. All rights reserved.
 */

package com.element.plaf.basic;

import com.element.plaf.JideButtonUI;
import com.element.plaf.LookAndFeelFactory;
import com.element.plaf.UIDefaultsLookup;
import com.element.ui.button.JideButton;
import com.element.ui.icons.IconsFactory;
import com.element.util.UIUtil;

import javax.swing.*;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.InsetsUIResource;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicButtonListener;
import javax.swing.plaf.basic.BasicGraphicsUtils;
import javax.swing.plaf.basic.BasicHTML;
import javax.swing.plaf.synth.Region;
import javax.swing.plaf.synth.SynthContext;
import javax.swing.plaf.synth.SynthLookAndFeel;
import javax.swing.plaf.synth.SynthStyle;
import javax.swing.text.View;
import java.awt.*;

/**
 * JideButtonUI implementation
 */
public class BasicJideButtonUI extends JideButtonUI {
	// Visual constants
	protected int defaultTextIconGap;

	// Offset controlled by set method
	private int shiftOffset = 0;
	protected int defaultTextShiftOffset;

	// Has the shared instance defaults been initialized?
	private boolean defaults_initialized = false;

	private static final String propertyPrefix = "JideButton" + ".";

	protected ThemePainter _painter;

	protected Color _shadowColor;
	protected Color _darkShadowColor;
	protected Color _highlight;
	protected Color _lightHighlightColor;

	protected Color _focusColor;

	protected boolean _isFloatingIcon = false;

	// ********************************
	//          Create PLAF
	// ********************************

	@SuppressWarnings({"UnusedDeclaration"})
	public static ComponentUI createUI(JComponent c) {
		return new BasicJideButtonUI();
	}

	protected String getPropertyPrefix() {
		return propertyPrefix;
	}


	// ********************************
	//          Install PLAF
	// ********************************

	@Override
	public void installUI(JComponent c) {
		installDefaults((AbstractButton) c);
		installListeners((AbstractButton) c);
		installKeyboardActions((AbstractButton) c);
		BasicHTML.updateRenderer(c, ((AbstractButton) c).getText());
	}

	protected void installDefaults(AbstractButton b) {
		// load shared instance defaults
		Object o = UIDefaultsLookup.get("Theme.painter");
		if (o instanceof ThemePainter) {
			_painter = (ThemePainter) o;
		} else {
			_painter = new BasicPainter();
		}

		String pp = getPropertyPrefix();
		if (!defaults_initialized) {
			defaultTextIconGap = UIDefaultsLookup.getInt(pp + "textIconGap");
			defaultTextShiftOffset = UIDefaultsLookup.getInt(pp + "textShiftOffset");

			_focusColor = UIDefaultsLookup.getColor("Button.focus"); // use Button.focus since we didn't install JideButton.focus.

			// next four lines part of optimized component defaults installation
            /* defaultForeground = UIManagerLookup.getColor(pp + "foreground");
             defaultBackground = UIManagerLookup.getColor(pp + "background");
             defaultFont = UIManagerLookup.getFont(pp + "font");
             defaultBorder = UIManagerLookup.getBorder(pp + "border");*/

			_shadowColor = UIDefaultsLookup.getColor("JideButton.shadow");
			_darkShadowColor = UIDefaultsLookup.getColor("JideButton.darkShadow");
			_highlight = UIDefaultsLookup.getColor("JideButton.highlight");
			_lightHighlightColor = UIDefaultsLookup.getColor("JideButton.light");

			defaults_initialized = true;
		}

//        // set the following defaults on the button
//        if (b.isContentAreaFilled()) {
//            b.setOpaque(true);
//        }
//        else {
//            b.setOpaque(false);
//        }

		updateMargin(b);

		// *** begin optimized defaults install ***

// *** end optimized defaults install ***

		// old code below works for component defaults installation, but it is slow
		LookAndFeel.installColorsAndFont(b, pp + "background", pp + "foreground", pp + "font");
		LookAndFeel.installBorder(b, pp + "border");

		_isFloatingIcon = UIDefaultsLookup.getBoolean("Icon.floating");
	}

	protected void installListeners(AbstractButton b) {
		BasicButtonListener listener = createButtonListener(b);
		if (listener != null) {
			// put the listener in the button's client properties so that
			// we can get at it later
			b.putClientProperty(this, listener);

			b.addMouseListener(listener);
			b.addMouseMotionListener(listener);
			b.addFocusListener(listener);
			b.addPropertyChangeListener(listener);
			b.addChangeListener(listener);
		}
	}

	protected void installKeyboardActions(AbstractButton b) {
		BasicButtonListener listener = (BasicButtonListener) b.getClientProperty(this);
		if (listener != null) {
			listener.installKeyboardActions(b);
		}
	}


	// ********************************
	//         Uninstall PLAF
	// ********************************

	@Override
	public void uninstallUI(JComponent c) {
		uninstallKeyboardActions((AbstractButton) c);
		uninstallListeners((AbstractButton) c);
		uninstallDefaults((AbstractButton) c);
		BasicHTML.updateRenderer(c, "");
	}

	protected void uninstallKeyboardActions(AbstractButton b) {
		BasicButtonListener listener = (BasicButtonListener) b.getClientProperty(this);
		if (listener != null) {
			listener.uninstallKeyboardActions(b);
		}
	}

	protected void uninstallListeners(AbstractButton b) {
		BasicButtonListener listener = (BasicButtonListener) b.getClientProperty(this);
		b.putClientProperty(this, null);
		if (listener != null) {
			b.removeMouseListener(listener);
			b.removeMouseMotionListener(listener);
			b.removeFocusListener(listener);
			b.removeChangeListener(listener);
			b.removePropertyChangeListener(listener);
		}
	}

	@SuppressWarnings({"UnusedDeclaration"})
	protected void uninstallDefaults(AbstractButton b) {
		_painter = null;
		_focusColor = null;


		_shadowColor = null;
		_highlight = null;
		_lightHighlightColor = null;
		_darkShadowColor = null;
		defaults_initialized = false;
	}

	// ********************************
	//        Create Listeners
	// ********************************

	protected BasicButtonListener createButtonListener(AbstractButton b) {
		return new BasicJideButtonListener(b);
	}

	@SuppressWarnings({"UnusedDeclaration"})
	public int getDefaultTextIconGap(AbstractButton b) {
		return defaultTextIconGap;
	}

	protected Color getFocusColor() {
		return _focusColor;
	}

	/* These rectangles/insets are allocated once for all
	 * ButtonUI.paint() calls.  Re-using rectangles rather than
	 * allocating them in each paint call substantially reduced the time
	 * it took paint to run.  Obviously, this method can't be re-entered.
	 */
	protected static Rectangle viewRect = new Rectangle();
	protected static Rectangle textRect = new Rectangle();
	protected static Rectangle iconRect = new Rectangle();

	// ********************************
	//          Paint Methods
	// ********************************

	@Override
	public void paint(Graphics g, JComponent c) {
		AbstractButton b = (AbstractButton) c;
		boolean isHorizontal = UIUtil.getOrientationOf(c) != SwingConstants.VERTICAL;


		FontMetrics fm = c.getFontMetrics(c.getFont());

		Insets i = c.getInsets();

		viewRect.x = i.left;
		viewRect.y = i.top;
		viewRect.width = b.getWidth() - (i.right + viewRect.x);
		viewRect.height = b.getHeight() - (i.bottom + viewRect.y);

		textRect.x = textRect.y = textRect.width = textRect.height = 0;
		iconRect.x = iconRect.y = iconRect.width = iconRect.height = 0;

		paintBackground(g, b);

		Font f = c.getFont();
		g.setFont(f);

		// layout the text and icon
		String text = SwingUtilities.layoutCompoundLabel(c, fm, b.getText(), b.getIcon(),
				b.getVerticalAlignment(), b.getHorizontalAlignment(),
				b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
				viewRect, iconRect, textRect,
				// JDK PORTING HINT
				// JDK1.3: getIconTextGap, use defaultTextIconGap
				b.getText() == null ? 0 : b.getIconTextGap());
		// String text = UIUtil.layoutCompoundLabel(c, fm, b.getText(), b.getIcon(),
		// 		isHorizontal,
		// 		b.getVerticalAlignment(), b.getHorizontalAlignment(),
		// 		b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
		// 		viewRect, iconRect, textRect,
		// 		// JDK PORTING HINT
		// 		// JDK1.3: getIconTextGap, use defaultTextIconGap
		// 		b.getText() == null ? 0 : b.getIconTextGap()); // use the bigger one of both gaps. Not really the best way.

		clearTextShiftOffset();

		paintIcon(b, g);

		boolean highContrast = UIManager.getBoolean("Theme.highContrast");
		if (highContrast && UIUtil.getButtonState(b) == ThemePainter.STATE_PRESSED) {
			textRect.x += 1;
			textRect.y += 1;
		}
		if (text != null && !text.equals("")) {
			View v = (View) c.getClientProperty(BasicHTML.propertyKey);
			if (v != null) {
				v.paint(g, textRect);
			} else {
				paintText(g, b, textRect, text);
			}
		}
	}

	protected void paintIcon(AbstractButton b, Graphics g) {
		// Paint the Icon
		if (b.getIcon() != null) {
			Icon icon = getIcon(b);

			ButtonModel model = b.getModel();
			if (icon != null) {
				if (isFloatingIcon() && model.isEnabled()) {
					if (model.isRollover() && !model.isPressed() && !model.isSelected()) {
						if (!"true".equals(System.getProperty("shadingtheme", "false")) && b instanceof JideButton && ((JideButton) b).getButtonStyle() == JideButton.TOOLBAR_STYLE) {
							if (icon instanceof ImageIcon) {
								ImageIcon shadow = IconsFactory.createGrayImage(((ImageIcon) icon).getImage());
								shadow.paintIcon(b, g, iconRect.x + 1, iconRect.y + 1);
							} else {
								ImageIcon shadow = IconsFactory.createGrayImage(b, icon);
								shadow.paintIcon(b, g, iconRect.x + 1, iconRect.y + 1);
							}
							icon.paintIcon(b, g, iconRect.x - 1, iconRect.y - 1);
						} else {
							icon.paintIcon(b, g, iconRect.x, iconRect.y);
						}
					} else {
						icon.paintIcon(b, g, iconRect.x, iconRect.y);
					}
				} else {
					icon.paintIcon(b, g, iconRect.x, iconRect.y);
				}
			}
		}
	}

	protected Icon getIcon(AbstractButton b) {
		ButtonModel model = b.getModel();
		Icon icon = b.getIcon();
		Icon tmpIcon = null;
		if (!model.isEnabled()) {
			if (model.isSelected()) {
				tmpIcon = b.getDisabledSelectedIcon();
			} else {
				tmpIcon = b.getDisabledIcon();
			}

			// create default disabled icon
			if (tmpIcon == null) {
				if (icon instanceof ImageIcon) {
					icon = IconsFactory.createGrayImage(((ImageIcon) icon).getImage());
				} else {
					icon = IconsFactory.createGrayImage(b, icon);
				}
			}
		} else if (model.isPressed() && model.isArmed()) {
			tmpIcon = b.getPressedIcon();
			if (tmpIcon != null) {
				// revert back to 0 offset
				clearTextShiftOffset();
			}
		} else if (b.isRolloverEnabled() && model.isRollover()) {
			if (model.isSelected()) {
				tmpIcon = b.getRolloverSelectedIcon();
			} else {
				tmpIcon = b.getRolloverIcon();
			}
		} else if (model.isSelected()) {
			tmpIcon = b.getSelectedIcon();
		}

		if (tmpIcon != null) {
			icon = tmpIcon;
		}
		return icon;
	}

	protected boolean isFloatingIcon() {
		return _isFloatingIcon;
	}

	/**
	 * As of Java 2 platform v 1.4 this method should not be used or overridden. Use the paintText method which takes
	 * the AbstractButton argument.
	 *
	 * @param g        the Graphics instance
	 * @param c        the component
	 * @param textRect the rectangle of the text
	 * @param text     the text to paint
	 */
	protected void paintText(Graphics g, JComponent c, Rectangle textRect, String text) {
		AbstractButton b = (AbstractButton) c;
		boolean isHorizontal = UIUtil.getOrientationOf(c) != SwingConstants.VERTICAL;

		ButtonModel model = b.getModel();
		FontMetrics fm = c.getFontMetrics(c.getFont());
		// JDK PORTING HINT
		// JDK1.3: No getDisplayedMnemonicIndex, use getMnemonic
		int mnemonicIndex = b.getDisplayedMnemonicIndex();

		if (!isHorizontal) {
			Graphics2D g2d = (Graphics2D) g.create();
			g2d.rotate(Math.PI / 2);
			g2d.translate(0, -c.getWidth() + 1);

			/* Draw the Text */
			if (model.isEnabled()) {
				/* paint the text normally */
				g2d.setColor(getForegroundOfState(b));

				// JDK PORTING HINT
				// JDK1.3: No drawStringUnderlineCharAt, draw the string then draw the underline
				UIUtil.drawStringUnderlineCharAt(b, g2d, text, mnemonicIndex,
						textRect.y + getTextShiftOffset(),
						textRect.x + fm.getAscent() + getTextShiftOffset());
			} else {
				/* paint the text disabled ***/
				/* paint the text disabled ***/
				Color color = UIDefaultsLookup.getColor("Button.disabledForeground");
				g2d.setColor(color == null ? b.getBackground().darker() : color);

				// JDK PORTING HINT
				// JDK1.3: No drawStringUnderlineCharAt, draw the string then draw the underline
				UIUtil.drawStringUnderlineCharAt(b, g2d, text, mnemonicIndex,
						textRect.y, textRect.x + fm.getAscent());
			}
			if (b instanceof JideButton && ((JideButton) b).getButtonStyle() == JideButton.HYPERLINK_STYLE
					&& (((JideButton) b).isAlwaysShowHyperlink() || b.getModel().isRollover())) {
				g.drawLine(textRect.x, textRect.y, textRect.x, textRect.y + textRect.height);
			}

			g2d.dispose();
		} else {
			/* Draw the Text */
			Color old = g.getColor();
			if (model.isEnabled()) {
				/* paint the text normally */
				g.setColor(getForegroundOfState(b));

				// JDK PORTING HINT
				// JDK1.3: No drawStringUnderlineCharAt, draw the string then draw the underline
				UIUtil.drawStringUnderlineCharAt(b, g, text, mnemonicIndex,
						textRect.x + getTextShiftOffset(),
						textRect.y + fm.getAscent() + getTextShiftOffset());
			} else {
				/* paint the text disabled ***/
				Color color = UIDefaultsLookup.getColor("Button.disabledForeground");
				g.setColor(color == null ? b.getBackground().darker() : color);

				// JDK PORTING HINT
				// JDK1.3: No drawStringUnderlineCharAt, draw the string then draw the underline
				UIUtil.drawStringUnderlineCharAt(b, g, text, mnemonicIndex,
						textRect.x, textRect.y + fm.getAscent());
			}
			if (b instanceof JideButton && ((JideButton) b).getButtonStyle() == JideButton.HYPERLINK_STYLE
					&& (((JideButton) b).isAlwaysShowHyperlink() || b.getModel().isRollover())) {
				g.drawLine(textRect.x, textRect.y + textRect.height - 2, textRect.x + textRect.width, textRect.y + textRect.height - 2);
			}
			g.setColor(old);
		}
	}

	protected Color getForegroundOfState(AbstractButton b) {
		int state = UIUtil.getButtonState(b);
		Color foreground = null;
		if (b instanceof ComponentStateSupport) {
			foreground = ((ComponentStateSupport) b).getForegroundOfState(state);
		}
		if (foreground == null || foreground instanceof UIResource) {
			foreground = b.getForeground();
		}
		return foreground;
	}

	/**
	 * Method which renders the text of the current button.
	 * <p/>
	 *
	 * @param g        Graphics context
	 * @param b        Current button to render
	 * @param textRect Bounding rectangle to render the text.
	 * @param text     String to render
	 * @since 1.4
	 */
	protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, String text) {
		paintText(g, (JComponent) b, textRect, text);
	}

	protected void paintBackground(Graphics g, AbstractButton b) {
		boolean paintDefaultBorder = true;
		boolean paintBackground;
		Object o = b.getClientProperty("JideButton.paintDefaultBorder");
		if (o instanceof Boolean) {
			paintDefaultBorder = (Boolean) o;
		}
		o = b.getClientProperty("JideButton.alwaysPaintBackground");
		if (o instanceof Boolean) {
			paintBackground = (Boolean) o;
		} else {
			paintBackground = b.isOpaque();
		}

		if (paintBackground) {
			g.setColor(b.getBackground());
			g.fillRect(0, 0, b.getWidth(), b.getHeight());
		}

		if (b.isContentAreaFilled()) {
			if (b instanceof JideButton && ((JideButton) b).getButtonStyle() == JideButton.TOOLBAR_STYLE) {
				Rectangle rect = new Rectangle(0, 0, b.getWidth(), b.getHeight());
				int state = UIUtil.getButtonState(b);
				if (state != ThemePainter.STATE_DEFAULT) {
					getPainter().paintButtonBackground(b, g, rect, 0, state);
				} else {
					if (paintBackground) {
						getPainter().paintButtonBackground(b, g, rect, UIUtil.getOrientationOf(b), state);
						if ("true".equals(System.getProperty("shadingtheme", "false"))) {
							UIUtil.fillGradient(g, rect, UIUtil.getOrientationOf(b));
						}
					}
				}
			} else if (b instanceof JideButton && ((JideButton) b).getButtonStyle() == JideButton.FLAT_STYLE) {
				paintBackgroundInFlatStyle(g, b, paintBackground);
			} else if (b instanceof JideButton && ((JideButton) b).getButtonStyle() == JideButton.TOOLBOX_STYLE) {
				paintBackgroundInToolboxStyle(g, b, paintBackground, paintDefaultBorder);
			}
		}
	}

	private void paintBackgroundInFlatStyle(Graphics g, AbstractButton b, boolean paintBackground) {
		Rectangle rect = new Rectangle(0, 0, b.getWidth(), b.getHeight());
		int state = UIUtil.getButtonState(b);
		switch (state) {
			case ThemePainter.STATE_SELECTED:
			case ThemePainter.STATE_PRESSED:
				UIUtil.paintBackground(g, rect, _highlight, _highlight);
				g.setColor(_shadowColor);    // inner 3D border
				g.drawLine(rect.x, rect.y, rect.width - 1, rect.y);
				g.drawLine(rect.x, rect.y, rect.x, rect.height - 1);

				g.setColor(_lightHighlightColor);     // black drop shadow  __|
				g.drawLine(rect.x, rect.height - 1, rect.width - 1, rect.height - 1);
				g.drawLine(rect.width - 1, rect.y, rect.width - 1, rect.height - 1);
				break;

			case ThemePainter.STATE_ROLLOVER:
				UIUtil.paintBackground(g, rect, _highlight, _highlight);
				g.setColor(_lightHighlightColor);    // inner 3D border
				g.drawLine(rect.x, rect.y, rect.width - 1, rect.y);
				g.drawLine(rect.x, rect.y, rect.x, rect.height - 1);

				g.setColor(_shadowColor);     // black drop shadow  __|
				g.drawLine(rect.x, rect.height - 1, rect.width - 1, rect.height - 1);
				g.drawLine(rect.width - 1, rect.y, rect.width - 1, rect.height - 1);
				break;
			case ThemePainter.STATE_DISABLE:
			case ThemePainter.STATE_DISABLE_SELECTED:
				if (paintBackground) {
					UIUtil.paintBackground(g, rect, _highlight, _highlight);
					g.setColor(_lightHighlightColor);    // inner 3D border
					g.drawLine(rect.x, rect.y, rect.width - 1, rect.y);
					g.drawLine(rect.x, rect.y, rect.x, rect.height - 1);

					g.setColor(_shadowColor);     // black drop shadow  __|
					g.drawLine(rect.x, rect.height - 1, rect.width - 1, rect.height - 1);
					g.drawLine(rect.width - 1, rect.y, rect.width - 1, rect.height - 1);
				}
				break;
			case ThemePainter.STATE_DEFAULT:
				if (paintBackground) {
					getPainter().paintButtonBackground(b, g, rect, UIUtil.getOrientationOf(b), state);
				}
				break;
		}
	}

	private void paintBackgroundInToolboxStyle(Graphics g, AbstractButton b, boolean paintBackground, boolean paintDefaultBorder) {
		Rectangle rect = new Rectangle(0, 0, b.getWidth(), b.getHeight());
		if (b.getModel().isPressed()) {
			getPainter().paintButtonBackground(b, g, rect, UIUtil.getOrientationOf(b), ThemePainter.STATE_PRESSED);
			if (paintDefaultBorder) {
				g.setColor(_darkShadowColor);    // inner 3D border
				g.drawLine(0, 0, b.getWidth() - 2, 0);
				g.drawLine(0, 0, 0, b.getHeight() - 2);

				g.setColor(_shadowColor);    // inner 3D border
				g.drawLine(1, 1, b.getWidth() - 3, 1);
				g.drawLine(1, 1, 1, b.getHeight() - 3);

				g.setColor(_lightHighlightColor);     // black drop shadow  __|
				g.drawLine(0, b.getHeight() - 1, b.getWidth() - 1, b.getHeight() - 1);
				g.drawLine(b.getWidth() - 1, 0, b.getWidth() - 1, b.getHeight() - 1);
			}
		} else if (b.getModel().isSelected() && b.getModel().isRollover()) {
			getPainter().paintButtonBackground(b, g, rect, UIUtil.getOrientationOf(b), ThemePainter.STATE_PRESSED);
			if (paintDefaultBorder) {
				g.setColor(_darkShadowColor);    // inner 3D border
				g.drawLine(0, 0, b.getWidth() - 2, 0);
				g.drawLine(0, 0, 0, b.getHeight() - 2);

				g.setColor(_shadowColor);    // inner 3D border
				g.drawLine(1, 1, b.getWidth() - 3, 1);
				g.drawLine(1, 1, 1, b.getHeight() - 3);

				g.setColor(_lightHighlightColor);     // black drop shadow  __|
				g.drawLine(0, b.getHeight() - 1, b.getWidth() - 1, b.getHeight() - 1);
				g.drawLine(b.getWidth() - 1, 0, b.getWidth() - 1, b.getHeight() - 1);
			}
		} else if (b.getModel().isSelected()) {
			getPainter().paintButtonBackground(b, g, rect, UIUtil.getOrientationOf(b), ThemePainter.STATE_SELECTED);
			if (paintDefaultBorder) {
				g.setColor(_darkShadowColor);    // inner 3D border
				g.drawLine(0, 0, b.getWidth() - 2, 0);
				g.drawLine(0, 0, 0, b.getHeight() - 2);

				g.setColor(_shadowColor);    // inner 3D border
				g.drawLine(1, 1, b.getWidth() - 3, 1);
				g.drawLine(1, 1, 1, b.getHeight() - 3);

				g.setColor(_lightHighlightColor);     // black drop shadow  __|
				g.drawLine(0, b.getHeight() - 1, b.getWidth() - 1, b.getHeight() - 1);
				g.drawLine(b.getWidth() - 1, 0, b.getWidth() - 1, b.getHeight() - 1);
			}
		} else if (b.getModel().isRollover() || (b.hasFocus() && b.isFocusPainted())) {
			getPainter().paintButtonBackground(b, g, rect, UIUtil.getOrientationOf(b), ThemePainter.STATE_ROLLOVER);
			if (paintDefaultBorder) {
				g.setColor(_lightHighlightColor);    // inner 3D border
				g.drawLine(0, 0, b.getWidth() - 1, 0);
				g.drawLine(0, 0, 0, b.getHeight() - 1);

				g.setColor(_shadowColor);     // gray drop shadow  __|
				g.drawLine(1, b.getHeight() - 2, b.getWidth() - 2, b.getHeight() - 2);
				g.drawLine(b.getWidth() - 2, 1, b.getWidth() - 2, b.getHeight() - 2);

				g.setColor(_darkShadowColor);     // black drop shadow  __|
				g.drawLine(0, b.getHeight() - 1, b.getWidth() - 1, b.getHeight() - 1);
				g.drawLine(b.getWidth() - 1, 0, b.getWidth() - 1, b.getHeight() - 1);
			}
		} else {
			if (paintBackground) {
				getPainter().paintButtonBackground(b, g, rect, UIUtil.getOrientationOf(b), ThemePainter.STATE_DEFAULT);
			} else {
				g.setColor(_lightHighlightColor);    // inner 3D border
				g.drawLine(0, 0, b.getWidth() - 1, 0);
				g.drawLine(0, 0, 0, b.getHeight() - 1);

				g.setColor(_shadowColor);     // black drop shadow  __|
				g.drawLine(0, b.getHeight() - 1, b.getWidth() - 1, b.getHeight() - 1);
				g.drawLine(b.getWidth() - 1, 0, b.getWidth() - 1, b.getHeight() - 1);
			}
		}

		if (paintBackground) {
			g.setColor(_lightHighlightColor);    // inner 3D border
			g.drawLine(0, 0, b.getWidth() - 1, 0);
			g.drawLine(0, 0, 0, b.getHeight() - 1);

			g.setColor(_shadowColor);     // black drop shadow  __|
			g.drawLine(0, b.getHeight() - 1, b.getWidth() - 1, b.getHeight() - 1);
			g.drawLine(b.getWidth() - 1, 0, b.getWidth() - 1, b.getHeight() - 1);
		}
	}

	protected void clearTextShiftOffset() {
		this.shiftOffset = 0;
	}

	protected void setTextShiftOffset() {
		this.shiftOffset = defaultTextShiftOffset;
	}

	protected int getTextShiftOffset() {
		return shiftOffset;
	}

	// ********************************
	//          Layout Methods
	// ********************************

	@Override
	public Dimension getMinimumSize(JComponent c) {
		Dimension d = getPreferredSize(c);
		View v = (View) c.getClientProperty(BasicHTML.propertyKey);
		if (v != null) {
			if (UIUtil.getOrientationOf(c) == SwingConstants.HORIZONTAL)
				d.width -= v.getPreferredSpan(View.X_AXIS) - v.getMinimumSpan(View.X_AXIS);
			else // TODO: not sure if this is correct
				d.height -= v.getPreferredSpan(View.X_AXIS) - v.getMinimumSpan(View.X_AXIS);
		}
		return d;
	}

	@Override
	public Dimension getPreferredSize(JComponent c) {
		AbstractButton b = (AbstractButton) c;

		Dimension d = BasicGraphicsUtils.getPreferredButtonSize(b, b.getIconTextGap());
		if (UIUtil.getOrientationOf(c) == SwingConstants.HORIZONTAL) {
			return d;
		} else {
			return new Dimension(d.height, d.width); // swap width and height
		}
	}

	@Override
	public Dimension getMaximumSize(JComponent c) {
		Dimension d = getPreferredSize(c);
		View v = (View) c.getClientProperty(BasicHTML.propertyKey);
		if (v != null) {
			d.width += v.getMaximumSpan(View.X_AXIS) - v.getPreferredSpan(View.X_AXIS);
		}
		return d;
	}

	public ThemePainter getPainter() {
		return _painter;
	}

	protected void updateMargin(AbstractButton b) {
		String pp = getPropertyPrefix();
		if (LookAndFeelFactory.isLnfInUse(LookAndFeelFactory.SYNTH_LNF)) {
			SynthStyle ss = SynthLookAndFeel.getStyle(b, Region.BUTTON);
			SynthContext sc = new SynthContext(b, Region.BUTTON, ss, 0);
			Insets insets = ss.getInsets(sc, new InsetsUIResource(0, 0, 0, 0));
			if (insets != null) {
				b.setMargin(insets);
			}
		} else if (b.getMargin() == null || (b.getMargin() instanceof UIResource)) {
			if (shouldWrapText(b)) {
				b.setMargin(UIDefaultsLookup.getInsets(pp + "margin.vertical"));
			} else {
				b.setMargin(UIDefaultsLookup.getInsets(pp + "margin"));
			}
		}
	}

	/**
	 * Checks if we should wrap text on a button. If the vertical text position is bottom and horizontal text position
	 * is center, we will wrap the text.
	 *
	 * @param c the component
	 * @return true or false.
	 */
	@SuppressWarnings({"UnusedDeclaration"})
	public static boolean shouldWrapText(Component c) {
		// return false for now before we support the text wrapping
		return false;

//        boolean wrapText = false;
//        if (c instanceof AbstractButton) {
//            if (((AbstractButton) c).getVerticalTextPosition() == SwingConstants.BOTTOM && ((AbstractButton) c).getHorizontalTextPosition() == SwingConstants.CENTER) {
//                wrapText = true;
//            }
//        }
//        return wrapText;
	}
}
